﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;

namespace IOA.MES.Common
{
    public class FtpUtils
    {
        // 默认常量定义
        private static readonly string rootPath = "/";
        private static readonly int defaultReadWriteTimeout = 300000;
        private static readonly int defaultFtpPort = 21;

        #region 设置初始化参数
        private string host = string.Empty;
        public string Host
        {
            get
            {
                return this.host ?? string.Empty;
            }
        }

        private string username = string.Empty;
        public string Username
        {
            get
            {
                return this.username;
            }
        }

        private string password = string.Empty;
        public string Password
        {
            get
            {
                return this.password;
            }
        }

        private int port = defaultFtpPort;
        public int Port
        {
            get
            {
                return port;
            }
            set
            {
                this.port = value;
            }
        }

        private bool usePassive = true;
        public bool UsePassive
        {
            get
            {
                return usePassive;
            }
            set
            {
                this.usePassive = value;
            }
        }

        private bool useBinary = true;
        public bool UserBinary
        {
            get
            {
                return useBinary;
            }
            set
            {
                this.useBinary = value;
            }
        }

        private int readWriteTimeout = defaultReadWriteTimeout;
        public int ReadWriteTimeout
        {
            get
            {
                return readWriteTimeout;
            }
            set
            {
                this.readWriteTimeout = value;
            }
        }
        #endregion

        #region 构造函数

        public FtpUtils(string username, string password)
            : this(string.Empty, username, password, defaultFtpPort, true, true, defaultReadWriteTimeout)
        {
        }

        public FtpUtils(string host, string username, string password)
            : this(host, username, password, defaultFtpPort, true, true, defaultReadWriteTimeout)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="host">主机名</param>
        /// <param name="username">用户名</param>
        /// <param name="password">密码</param>
        /// <param name="port">端口号 默认21</param>
        /// <param name="proxy">代理 默认没有</param>
        /// <param name="enableSsl">是否使用ssl 默认不用</param>
        /// <param name="useBinary">使用二进制</param>
        /// <param name="usePassive">获取或设置客户端应用程序的数据传输过程的行为</param>
        /// <param name="readWriteTimeout">读写超时时间 默认5min</param>
        public FtpUtils(string host, string username, string password, int port, bool useBinary, bool usePassive, int readWriteTimeout)
        {
            this.host = (host.StartsWith("ftp://", StringComparison.CurrentCultureIgnoreCase) ? host : "ftp://" + host).TrimEnd('/');
            this.username = username;
            this.password = password;
            this.port = port;
            this.useBinary = useBinary;
            this.usePassive = usePassive;
            this.readWriteTimeout = readWriteTimeout;
        }
        #endregion

        /// <summary>
        /// 拼接URL
        /// </summary>
        /// <param name="host">主机名</param>
        /// <param name="remotePath">地址</param>
        /// <param name="fileName">文件名</param>
        /// <returns>返回完整的URL</returns>
        private string UrlCombine(string host, string fileName)
        {
            string result = $"{host.TrimEnd('/')}{fileName}";
            return result;
        }

        /// <summary>
        /// 创建连接
        /// </summary>
        /// <param name="url">地址</param>
        /// <param name="method">方法</param>
        /// <returns>返回 request对象</returns>
        private FtpWebRequest CreateConnection(string url, string method)
        {
            FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(new Uri(url));
            request.Credentials = new NetworkCredential(this.username, this.password);
            request.KeepAlive = false;
            request.UseBinary = useBinary;
            request.UsePassive = usePassive;
            request.Method = method;
            Console.WriteLine(request);
            return request;
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="localFile">本地文件</param>
        /// <param name="remoteFileName">上传文件名</param>
        /// <returns>上传成功返回 true</returns>
        public bool Upload(FileInfo localFile, string remoteFileName)
        {
            bool result = false;
            if (localFile.Exists)
            {
                try
                {
                    string url = UrlCombine(Host, remoteFileName);
                    FtpWebRequest request = CreateConnection(url, WebRequestMethods.Ftp.UploadFile);

                    using (Stream rs = request.GetRequestStream())
                    using (FileStream fs = localFile.OpenRead())
                    {
                        byte[] buffer = new byte[1024 * 4];
                        int count = fs.Read(buffer, 0, buffer.Length);
                        while (count > 0)
                        {
                            rs.Write(buffer, 0, count);
                            count = fs.Read(buffer, 0, buffer.Length);
                        }
                        fs.Close();
                        result = true;
                    }
                }
                catch (WebException ex)
                {
                    // MessageBox.Show(ex.Message);
                }
                return result;
            }
            // 处理本地文件不存在的情况
            return false;
        }


        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="localFile">本地文件</param>
        /// <param name="remoteFileName">上传文件名</param>
        /// <returns>上传成功返回 true</returns>
        public bool Upload(byte[] buffer, string remoteFileName)
        {
            bool result = false;
            try
            {
                string url = UrlCombine(Host, remoteFileName);
                var request = CreateConnection(url, WebRequestMethods.Ftp.UploadFile);

                using (Stream rs = request.GetRequestStream())
                {
                    rs.Write(buffer, 0, buffer.Length);
                    result = true;
                }
            }
            catch (WebException ex)
            {
                // MessageBox.Show(ex.Message);
            }
            return result;
        }

        /// <summary>
        /// 检查并创建文件夹
        /// </summary>
        /// <param name="directory"></param>
        public void CheckAndCreateDirectory(string directory)
        {
            var directories = directory.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            directories.Insert(0, "");

            var nextDir = string.Empty;
            for (var i = 0; i < directories.Count - 1; i++)
            {
                var children = GetFileList($"{host}/{nextDir}");
                nextDir = string.IsNullOrWhiteSpace(nextDir) ? directories[i + 1] : $"{nextDir}/{directories[i + 1]}";
                if (!children.Any(_ => string.Equals(_, $"{directories[i]}/{directories[i + 1]}".Trim('/'), StringComparison.CurrentCultureIgnoreCase)))
                {
                    CreateDirectory($"/{nextDir}");
                }
            }
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="serverName">服务器文件名称</param>
        /// <param name="localName">需要保存在本地的文件名称</param>
        /// <returns>下载成功返回 true</returns>
        public bool Download(string serverName, string localName)
        {
            if (string.IsNullOrWhiteSpace(host))
            {
                host = serverName.Substring(0, serverName.IndexOf("/", serverName.IndexOf("/", 6) + 1));
            }

            bool result = false;
            var folder = Path.GetDirectoryName(localName);
            if (!Directory.Exists(folder))
            {
                Directory.CreateDirectory(folder);
            }
            using (FileStream fs = new FileStream(localName, FileMode.OpenOrCreate))
            {
                try
                {
                    FtpWebRequest request = CreateConnection(serverName, WebRequestMethods.Ftp.DownloadFile);
                    request.ContentOffset = fs.Length;
                    using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                    {
                        fs.Position = fs.Length;
                        byte[] buffer = new byte[1024 * 4];
                        int count = response.GetResponseStream().Read(buffer, 0, buffer.Length);
                        while (count > 0)
                        {
                            fs.Write(buffer, 0, count);
                            count = response.GetResponseStream().Read(buffer, 0, buffer.Length);
                        }
                        response.GetResponseStream().Close();
                    }
                    result = true;
                }
                catch (WebException ex)
                {
                    // 处理ftp连接中的异常
                }
            }
            return result;
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="serverName">服务器文件名称</param>
        /// <param name="localName">需要保存在本地的文件名称</param>
        /// <returns>下载成功返回 true</returns>
        public byte[] Download(string serverName)
        {
            if (string.IsNullOrWhiteSpace(host))
            {
                host = serverName.Substring(0, serverName.IndexOf("/", serverName.IndexOf("/", 6) + 1));
            }

            byte[] result;
            using (MemoryStream stream = new MemoryStream())
            {
                var request = CreateConnection(serverName, WebRequestMethods.Ftp.DownloadFile);
                request.ContentOffset = stream.Length;
                using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                {
                    stream.Position = stream.Length;
                    var buffer = new byte[1024 * 10];
                    int count = response.GetResponseStream().Read(buffer, 0, buffer.Length);
                    while (count > 0)
                    {
                        stream.Write(buffer, 0, count);
                        count = response.GetResponseStream().Read(buffer, 0, buffer.Length);
                    }
                    response.GetResponseStream().Close();
                }
                stream.Position = 0;
                result = new byte[(int)stream.Length];
                stream.Read(result, 0, result.Length);
            }
            return result;
        }

        private void CreateDirectory(string path)
        {
            var createRequest = CreateConnection(UrlCombine(Host, path), WebRequestMethods.Ftp.MakeDirectory);
            createRequest.GetResponse().Close();
        }

        private List<string> GetFileList(string path)
        {
            var directories = new List<string>();
            try
            {
                var request = CreateConnection(path, WebRequestMethods.Ftp.ListDirectory);
                var response = request.GetResponse();
                var reader = new StreamReader(response.GetResponseStream(), Encoding.Default);//中文文件名
                string line = reader.ReadLine();

                while (line != null)
                {
                    directories.Add(line);
                    line = reader.ReadLine();
                }
                reader.Close();
                response.Close();
            }
            catch (Exception ex)
            {
                throw new Exception("获取文件列表失败。原因： " + ex.Message);
            }
            return directories;
        }
    }
}
